1 코로나 확진자 데이터1

공공데이터포털 웹사이트에서 코로나19 감염 현황에 대한 API를 제공하고 있다. 전반적인 감염현황과 함께 연령별, 성별, 시도별 현황 정보도 함께 제공하고 있다.

2 감염현황 데이터

먼저 감염현황에 대한 데이터를 가져온다. 공공데이터포털에서 발급된 API KEY는 usethis::use_r_environ() 명령어로 외부에 노출되지 않도록 .Renviron 환경변수에 저장하여 Sys.getenv('COVID_APIKEY') 명령어로 API로 호출시 활용한다.

library(tidyverse)
library(httr)
library(rvest)
library(glue)

readRenviron("~/.Renviron")

covid_confirmed_api <- glue("http://openapi.data.go.kr/openapi/service/rest/Covid19/getCovid19InfStateJson",
                  "?serviceKey      = {Sys.getenv('COVID_APIKEY')}",
                  "&pageNo          = 1",
                  "&numOfRows       = 10",
                  "&startCreateDt   = 20200410",
                  "&endCreateDt     = 20200410") %>% str_remove_all(., " ")

covid_confirmed_resp <- GET(covid_confirmed_api) %>% 
  content(.) 

covid_confirmed_resp %>%
  listviewer::jsonedit()

특정 날짜(“2020-04-10”)에 대해 확진자 현황 데이터를 얻게 되었다면 다음 단계로 JSON 자료형을 데이터프레임으로 변환시킨다. “공공데이터 오픈API 활용가이드” 에 나온 응답결과에 대한 명세서에 나온 주요 필드값은 다음과 같다.

  • ACC_EXAM_CNT : 누적 검사 수
  • ACC_EXAM_COMP_CNT: 누적 검사 완료 수
  • ACC_DEF_RATE : 누적 환진률
  • DECIDE_CNT : 확진자 수
  • RESUTL_NEG_CNT : 결과 음성 수
  • EXAM_CNT : 검사진행 수
  • DEATH_CNT : 사망자 수
  • CARE_CNT : 치료중 환자 수
  • CLEAR_CNT : 격리해제 수
covid_confirmed_df <- tibble(
  "날짜"           = map_chr(covid_confirmed_resp$response$body$items, "createDt"),
  "누적검사자"     = map_chr(covid_confirmed_resp$response$body$items, "accExamCnt"),
  "누적확진자"     = map_chr(covid_confirmed_resp$response$body$items, "accExamCompCnt"),
  "누적사망율"     = map_chr(covid_confirmed_resp$response$body$items, "accDefRate"),
    
  "검사자"         = map_chr(covid_confirmed_resp$response$body$items, "examCnt"),
  "확진자"         = map_chr(covid_confirmed_resp$response$body$items, "decideCnt"),
  
  "누적사망자"     = map_chr(covid_confirmed_resp$response$body$items, "deathCnt"),
  "치료중"         = map_chr(covid_confirmed_resp$response$body$items, "careCnt"),  
  
  "누적격리해제"   = map_chr(covid_confirmed_resp$response$body$items, "clearCnt"),
  "누적음성판정"   = map_chr(covid_confirmed_resp$response$body$items, "resutlNegCnt")
)

covid_confirmed_df

기간을 확대하여 전체 기간으로 코로나바이러스감염증 감염현황 데이터를 추출한다.

covid_confirmed_api <- glue("http://openapi.data.go.kr/openapi/service/rest/Covid19/getCovid19InfStateJson",
                  "?serviceKey      = {Sys.getenv('COVID_APIKEY')}",
                  "&pageNo          = 1",
                  "&numOfRows       = 10",
                  "&startCreateDt   = 20200101",
                  "&endCreateDt     = 20201226") %>% str_remove_all(., " ")

covid_confirmed_resp <- GET(covid_confirmed_api) %>% 
  content(.) 


covid_confirmed_df <- tibble(
  "날짜"           = map_chr(covid_confirmed_resp$response$body$items$item, "createDt", .default = NA),
  "누적검사자"     = map_chr(covid_confirmed_resp$response$body$items$item, "accExamCnt", .default = NA),
  "누적확진자"     = map_chr(covid_confirmed_resp$response$body$items$item, "accExamCompCnt", .default = NA),
  "누적사망율"     = map_chr(covid_confirmed_resp$response$body$items$item, "accDefRate", .default = NA),
  "검사자"         = map_chr(covid_confirmed_resp$response$body$items$item, "examCnt", .default = NA),
  "확진자"         = map_chr(covid_confirmed_resp$response$body$items$item, "decideCnt", .default = NA),
  "누적사망자"     = map_chr(covid_confirmed_resp$response$body$items$item, "deathCnt", .default = NA),
  "치료중"         = map_chr(covid_confirmed_resp$response$body$items$item, "careCnt", .default = NA),  
  "누적격리해제"   = map_chr(covid_confirmed_resp$response$body$items$item, "clearCnt", .default = NA),
  "누적음성판정"   = map_chr(covid_confirmed_resp$response$body$items$item, "resutlNegCnt", .default = NA)
)

covid_confirmed_df %>% 
  write_rds("data/covid_confirmed_api_orig.rds")

covid_confirmed_df %>% 
  arrange(날짜) %>% 
  reactable::reactable()

3 성별, 연령별

먼저 특정일(2020-12-20) 기준 데이터를 추출해보자. 공공데이터 포털에 API를 요청하게 되면 결과값이 JSON으로 반환되어 먼저 자료형을 일별한다.

covid_gender_api <- glue("http://openapi.data.go.kr/openapi/service/rest/Covid19/getCovid19GenAgeCaseInfJson",
                  "?serviceKey      = {Sys.getenv('COVID_APIKEY')}",
                  "&pageNo          = 1",
                  "&numOfRows       = 10",
                  "&startCreateDt   = 20201220",
                  "&endCreateDt     = 20201220") %>% str_remove_all(., " ")

covid_resp <- GET(covid_gender_api) %>% 
  content(.) 

covid_resp %>% 
  listviewer::jsonedit()

JSON을 데이터프레임으로 변환시킨다. 성별과 연령대가 뒤섞여 있기 때문에 성별과 연령 데이터프레임으로 나눈다.

covid_dat <- tibble(
  "날짜"   = map_chr(covid_resp$response$body$items$item, "createDt"),
  "확진자" = map_int(covid_resp$response$body$items$item, "confCase"),
  "감염율" = map_chr(covid_resp$response$body$items$item, "confCaseRate"),
  "사망자" = map_int(covid_resp$response$body$items$item, "death"),
  "치명율" = map_chr(covid_resp$response$body$items$item, "deathRate"),
  "구분"   = map_chr(covid_resp$response$body$items$item, "gubun")
)

covid_dat
# A tibble: 11 x 6
   날짜                    확진자 감염율    사망자 치명율    구분   
   <chr>                    <int> <chr>      <int> <chr>     <chr>  
 1 2020-12-20 14:14:21.025   1638 3.300000       0 0.00      0-9    
 2 2020-12-20 14:14:21.024   3105 6.250000       0 0.00      10-19  
 3 2020-12-20 14:14:21.024   8455 17.020000      0 0.00      20-29  
 4 2020-12-20 14:14:21.024   6297 12.680000      2 0.30      30-39  
 5 2020-12-20 14:14:21.024   6975 14.040000      6 0.890000  40-49  
 6 2020-12-20 14:14:21.024   9207 18.540000     28 4.150000  50-59  
 7 2020-12-20 14:14:21.024   7722 15.550000     81 12.020000 60-69  
 8 2020-12-20 14:14:21.024   3892 7.840000     200 29.670000 70-79  
 9 2020-12-20 14:14:21.023   2374 4.780000     357 52.970000 80 이상
10 2020-12-20 14:14:21.023  25797 51.940000    329 48.810000 여성   
11 2020-12-20 14:14:21.023  23868 48.060000    345 51.190000 남성   

하루를 넘어 전체 기간에 대해서 연령별, 성별 코로나19 데이터를 가져온다. 2020-04-01 부터 데이터를 제공하기 때문에 시작일을 2020-04-01 으로 특정하여 현재까지 데이터를 가져온다.

library(lubridate)

# 전체 데이터 -----
covid_age_gender_api <- glue("http://openapi.data.go.kr/openapi/service/rest/Covid19/getCovid19GenAgeCaseInfJson",
                  "?serviceKey      = {Sys.getenv('COVID_APIKEY')}",
                  "&pageNo          = 1",
                  "&numOfRows       = 10",
                  "&startCreateDt   = 20200401",
                  "&endCreateDt     = 20201225") %>% str_remove_all(., " ")

covid_age_gender_resp <- GET(covid_age_gender_api) %>% 
  content(.) 

covid_age_gender_df <- tibble(
  "날짜"   = map_chr(covid_age_gender_resp$response$body$items$item, "createDt"),
  "확진자" = map_int(covid_age_gender_resp$response$body$items$item, "confCase"),
  "감염율" = map_chr(covid_age_gender_resp$response$body$items$item, "confCaseRate"),
  "사망자" = map_int(covid_age_gender_resp$response$body$items$item, "death"),
  "치명율" = map_chr(covid_age_gender_resp$response$body$items$item, "deathRate"),
  "구분"   = map_chr(covid_age_gender_resp$response$body$items$item, "gubun")
)

covid_age_gender_df %>% 
  write_rds("data/covid_age_gender.rds")

covid_age_gender_df
# A tibble: 2,861 x 6
   날짜                    확진자 감염율    사망자 치명율    구분   
   <chr>                    <int> <chr>      <int> <chr>     <chr>  
 1 2020-12-25 14:12:01.023   1844 3.370000       0 0.00      0-9    
 2 2020-12-25 14:12:01.023   3403 6.210000       0 0.00      10-19  
 3 2020-12-25 14:12:01.023   9042 16.510000      0 0.00      20-29  
 4 2020-12-25 14:12:01.023   6948 12.690000      3 0.390000  30-39  
 5 2020-12-25 14:12:01.023   7695 14.050000      7 0.910000  40-49  
 6 2020-12-25 14:12:01.023  10248 18.710000     30 3.880000  50-59  
 7 2020-12-25 14:12:01.022   8589 15.680000     90 11.640000 60-69  
 8 2020-12-25 14:12:01.022   4321 7.890000     227 29.370000 70-79  
 9 2020-12-25 14:12:01.022   2680 4.890000     416 53.820000 80 이상
10 2020-12-25 14:12:01.022  28235 51.550000    383 49.550000 여성   
# ... with 2,851 more rows

4 시도별

시도별로 코로나19 확진자 및 사망자 데이터를 추출한다. 공공데이터 포털에 API를 요청하게 되면 결과값이 JSON으로 반환되어 먼저 자료형을 일별한다.

covid_sido_api <- glue("http://openapi.data.go.kr/openapi/service/rest/Covid19/getCovid19SidoInfStateJson",
                  "?serviceKey      = {Sys.getenv('COVID_APIKEY')}",
                  "&pageNo          = 1",
                  "&numOfRows       = 10",
                  "&startCreateDt   = 20200410",
                  "&endCreateDt     = 20200410") %>% str_remove_all(., " ")

covid_sido_resp <- GET(covid_sido_api) %>% 
  content(.) 

covid_sido_resp %>% 
  listviewer::jsonedit()

JSON을 데이터프레임으로 변환시킨다. 이를 위해서 중요한 필드를 정의하면 다음과 같다.

  • CREATE_DT : 등록일시분초
  • DEATH_CNT : 사망자 수
  • GUBUN : 시도명(한글)
  • INC_DEC : 전일대비 증감 수
  • ISOL_CLEAR_CNT: 격리 해제 수
  • QUR_RATE : 10만명당 발생률
  • DEF_CNT : 확진자 수
  • ISOL_ING_CNT : 격리중 환자수
  • OVER_FLOW_CNT : 해외유입 수
  • LOCAL_OCC_CNT : 지역발생 수
covid_sido_dat <- tibble(
  "날짜"        = map_chr(covid_sido_resp$response$body$items$item, "createDt"),
  "시도"        = map_chr(covid_sido_resp$response$body$items$item, "gubun"),
  "확진자"      = map_chr(covid_sido_resp$response$body$items$item, "defCnt"),
  "해외유입"    = map_chr(covid_sido_resp$response$body$items$item, "overFlowCnt"),
  "지역발생"    = map_chr(covid_sido_resp$response$body$items$item, "localOccCnt"),  
  "10만_발생률" = map_chr(covid_sido_resp$response$body$items$item, "qurRate"),
  
  "누적사망자"  = map_chr(covid_sido_resp$response$body$items$item, "deathCnt"),
  "사망자"      = map_chr(covid_sido_resp$response$body$items$item, "incDec"),
  
  "격리해제"    = map_chr(covid_sido_resp$response$body$items$item, "isolClearCnt")
)

covid_sido_dat

하루를 넘어 전체 기간에 대해서 시도별 코로나19 데이터를 가져온다. 전체기간을 대상으로 데이터를 가져온다.

# 전체 데이터 -----
covid_sido_api <- glue("http://openapi.data.go.kr/openapi/service/rest/Covid19/getCovid19SidoInfStateJson",
                  "?serviceKey      = {Sys.getenv('COVID_APIKEY')}",
                  "&pageNo          = 1",
                  "&numOfRows       = 10",
                  "&startCreateDt   = 20200101",
                  "&endCreateDt     = 20201226") %>% str_remove_all(., " ")

covid_sido_resp <- GET(covid_sido_api) %>% 
  content(.) 

covid_sido_df <- tibble(
  "날짜"        = map_chr(covid_sido_resp$response$body$items$item, "createDt", .default = NA),
  "시도"        = map_chr(covid_sido_resp$response$body$items$item, "gubun", .default = NA),
  "확진자"      = map_chr(covid_sido_resp$response$body$items$item, "defCnt", .default = NA),
  "해외유입"    = map_chr(covid_sido_resp$response$body$items$item, "overFlowCnt", .default = NA),
  "지역발생"    = map_chr(covid_sido_resp$response$body$items$item, "localOccCnt", .default = NA),  
  "10만_발생률" = map_chr(covid_sido_resp$response$body$items$item, "qurRate", .default = NA),
  
  "누적사망자"  = map_chr(covid_sido_resp$response$body$items$item, "deathCnt", .default = NA),
  "사망자"      = map_chr(covid_sido_resp$response$body$items$item, "incDec", .default = NA),
  
  "격리해제"    = map_chr(covid_sido_resp$response$body$items$item, "isolClearCnt", .default = NA)
)

covid_sido_df %>% 
  write_rds("data/covid_sido_orig.rds")

covid_sido_df %>% 
  arrange(날짜) %>% 
  reactable::reactable()
 

데이터 과학자 이광춘 저작

kwangchun.lee.7@gmail.com